﻿#region Librerias

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

#endregion


namespace ESF.General.Commands
{
	// TODO: Pensar en manejar "secciones de objetos" o metodos especializados de redo y undo para evitar tener que copiar todo el objeto transformado cada vez
	// sino solo el pedazo que es de interes
	public class CommandManager<O>
	{
		#region Constantes

		public const int MAXCOMMANDCOUNT = 20;

		#endregion


		#region Miembros
		
		private List<ICommand <O>> _Commands;

		private int _CurrentCommandIndex;

		private int _MaxCommandCount;

		private O _OriginalObject;

		#endregion


		#region Propiedades

		public O OriginalObject
		{
			get
			{
				return _OriginalObject;
			}
		}


		public O ClonedOriginalObject
		{
			get
			{
				return ObjectHelper.Clone <O> (OriginalObject);
			}
		}


		public O CurrentObject
		{
			get
			{
				if (_Commands.Count == 0 || _CurrentCommandIndex == -1)
				{
					return _OriginalObject;
				}
				
				return _Commands [_CurrentCommandIndex].CurrentObject;
			}
		}


		#endregion


		#region Constructor

		public CommandManager (O pOriginalObject) : this (pOriginalObject, MAXCOMMANDCOUNT) {}


		public CommandManager (O pOriginalObject, int pMaxCommandCount)
		{
			_OriginalObject = pOriginalObject;

			_MaxCommandCount = pMaxCommandCount;

			_Commands = new List<ICommand<O>> ();

			Reset ();
		}

		#endregion


		#region Funciones

		public bool GetCurrentCommandParameter<T> (string pName, out T pValue)
		{
			pValue = default (T);

			if (_CurrentCommandIndex == -1) { return false; }

			if (_Commands [_CurrentCommandIndex].Parameters.ContainsKey (pName))
			{
				pValue = (T) _Commands [_CurrentCommandIndex].Parameters [pName];
				return true;
			}

			return false;
		}

		public void ClearAllExceptTheLast ()
		{
			ICommand <O> used;


			used = _Commands [_CurrentCommandIndex];
			
			_Commands.Clear ();

			_CurrentCommandIndex = 0;
			_Commands.Add (used);
		}


		public void Reset ()
		{
			_Commands.Clear ();

			_CurrentCommandIndex = -1;
		}


		public void Add (ICommand<O> pCommand)
		{
			// Habia comandos descartados mas alla de la ultima posicion, estos se remueven
			if (_Commands.Count > 0 && _CurrentCommandIndex != _Commands.Count - 1)
			{
				if (_CurrentCommandIndex == -1)
				{
					_Commands.Clear ();
				}
				else
				{
					_Commands.RemoveRange (_CurrentCommandIndex + 1, _Commands.Count - _CurrentCommandIndex - 1);
				}
			}

			// Se pasa del numero maximo de comandos, se debe remover los x - tope mas antiguos
			if (_Commands.Count + 1 > _MaxCommandCount)
			{
				_CurrentCommandIndex -= _Commands.Count - _MaxCommandCount;
				
				_Commands.RemoveRange (0, _Commands.Count - _MaxCommandCount);
			}

			pCommand.Execute (CurrentObject);
			
			_Commands.Add (pCommand);

			_CurrentCommandIndex = _Commands.Count - 1;			
		}


		public void RedoLast ()
		{
			Redo (1);
		}


		public void Redo (int pIndexCount)
		{
			Do (Math.Abs (pIndexCount));
		}


		public void UndoLast ()
		{
			Undo (1);
		}


		public void Undo (int pIndexCount)
		{
			if (_CurrentCommandIndex == -1) { return; }
			
			Do (-1 * Math.Abs (pIndexCount));
		}


		private void Do (int pIndexCount)
		{	 
			if (_CurrentCommandIndex + pIndexCount >= -1 && _CurrentCommandIndex + pIndexCount < _Commands.Count)
			{
				_CurrentCommandIndex += pIndexCount;
			}
		}

		#endregion
	}
}
